From e49cbf934dab8bf9e1becc5412fa279b1f720d2d Mon Sep 17 00:00:00 2001 From: robertl Date: Thu, 6 Aug 2009 03:19:10 +0000 Subject: [PATCH] Add serial port detection for Mac. --- gui/TODO | 12 +++ gui/app.pro | 4 +- gui/mainwindow.cpp | 28 +++--- gui/mainwindow.h | 3 +- gui/serial_mac.cpp | 219 +++++++++++++++++++++++++++++++++++++++++++++ gui/upgrade.cpp | 12 ++- 6 files changed, 263 insertions(+), 15 deletions(-) create mode 100644 gui/TODO create mode 100644 gui/serial_mac.cpp diff --git a/gui/TODO b/gui/TODO new file mode 100644 index 000000000..efd24a4fb --- /dev/null +++ b/gui/TODO @@ -0,0 +1,12 @@ +#1 Improve device names + a) switch format + delbin: device_name == "usb:" + garmin: device_name = "usb:" + available serial ports. + b) list only available serial ports (horribly OS specific) + +#2 A 'help' button to the right of formats that opens the online man page. + +#3 Upgrade needs dialog checkboxes for "never check again", "include beta" + and a button for "check now". + + diff --git a/gui/app.pro b/gui/app.pro index a78f585d6..2ed7251c0 100755 --- a/gui/app.pro +++ b/gui/app.pro @@ -1,4 +1,4 @@ -# $Id: app.pro,v 1.5 2009/07/27 15:36:30 robertl Exp $ +# $Id: app.pro,v 1.6 2009/08/06 03:19:10 robertl Exp $ # #CONFIG += qt debug console @@ -17,6 +17,7 @@ unix:OBJECTS_DIR = objects unix:RCC_DIR = objects win32:LIBS += SHELL32.LIB +mac:LIBS += -framework IOKit UI_DIR = tmp @@ -60,6 +61,7 @@ SOURCES += processwait.cpp SOURCES += filterwidgets.cpp SOURCES += filterdlg.cpp SOURCES += upgrade.cpp +mac:SOURCES += serial_mac.cpp HEADERS += mainwindow.h HEADERS += map.h diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index ac766e59a..80a265e9d 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -1,5 +1,5 @@ // -*- C++ -*- -// $Id: mainwindow.cpp,v 1.3 2009/08/03 05:16:23 robertl Exp $ +// $Id: mainwindow.cpp,v 1.4 2009/08/06 03:19:10 robertl Exp $ //------------------------------------------------------------------------ // // Copyright (C) 2009 S. Khai Mong . @@ -39,22 +39,22 @@ #include "gmapdlg.h" #include "upgrade.h" -#ifndef _WIN32 +#ifdef _WIN32 static const char *deviceNames[] = { "USB:", - "/dev/ttyS0", - "/dev/ttyS1", - "/dev/ttyS2", - "/dev/ttyS3", + "COM1:", + "COM2:", + "COM3:", + "COM4:", 0 }; #else static const char *deviceNames[] = { "USB:", - "COM1:", - "COM2:", - "COM3:", - "COM4:", + "/dev/ttyS0", + "/dev/ttyS1", + "/dev/ttyS2", + "/dev/ttyS3", 0 }; #endif @@ -202,10 +202,18 @@ void MainWindow::loadDeviceNameCombos() { ui.inputDeviceNameCombo->clear(); ui.outputDeviceNameCombo->clear(); +#if defined Q_OS_MAC + ui.inputDeviceNameCombo->addItem("usb:"); + ui.outputDeviceNameCombo->addItem("usb:"); + + osLoadDeviceNameCombos(ui.inputDeviceNameCombo); + osLoadDeviceNameCombos(ui.outputDeviceNameCombo); +#else for (int i=0; deviceNames[i]; i++) { ui.inputDeviceNameCombo->addItem(deviceNames[i]); ui.outputDeviceNameCombo->addItem(deviceNames[i]); } +#endif } //------------------------------------------------------------------------ void MainWindow::loadCharSetCombos() diff --git a/gui/mainwindow.h b/gui/mainwindow.h index be1ff2a49..c162a5c01 100644 --- a/gui/mainwindow.h +++ b/gui/mainwindow.h @@ -1,5 +1,5 @@ // -*- C++ -*- -// $Id: mainwindow.h,v 1.2 2009/07/31 18:32:32 robertl Exp $ +// $Id: mainwindow.h,v 1.3 2009/08/06 03:19:10 robertl Exp $ //------------------------------------------------------------------------ // // Copyright (C) 2009 S. Khai Mong . @@ -76,6 +76,7 @@ private: void setWidgetValues(); void getWidgetValues(); UpgradeCheck *upgrade; + void osLoadDeviceNameCombos(QComboBox*); protected: void closeEvent(QCloseEvent*); diff --git a/gui/serial_mac.cpp b/gui/serial_mac.cpp new file mode 100644 index 000000000..d20bd3d25 --- /dev/null +++ b/gui/serial_mac.cpp @@ -0,0 +1,219 @@ +// Borrowed liberally (as allowed by license) from +// http://developer.apple.com/samplecode/SerialPortSample/index.html +// This really is a slash-and-burn; no attempt was to make it very "C++-iike" + +// Apple's copyright blob: +/* + Copyright: © Copyright 2000-2005 Apple Computer, Inc. All rights reserved. + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs + copyrights in this original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks or logos of + Apple Computer, Inc. may be used to endorse or promote products derived from the + Apple Software without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any patent rights that + may be infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#if defined(MAC_OS_X_VERSION_10_3) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_3) +#include +#endif +#include + +#include "mainwindow.h" + +// Function prototypes +static kern_return_t FindModems(io_iterator_t *matchingServices); +static kern_return_t GetModemPath(io_iterator_t serialPortIterator, char *bsdPath, CFIndex maxPathSize); +static int OpenSerialPort(const char *bsdPath); +static char *LogString(char *str); +static Boolean InitializeModem(int fileDescriptor); +static void CloseSerialPort(int fileDescriptor); + +// Returns an iterator across all known modems. Caller is responsible for +// releasing the iterator when iteration is complete. +static kern_return_t FindModems(io_iterator_t *matchingServices) +{ + kern_return_t kernResult; + CFMutableDictionaryRef classesToMatch; + +/*! @function IOServiceMatching + @abstract Create a matching dictionary that specifies an IOService class match. + @discussion A very common matching criteria for IOService is based on its class. IOServiceMatching will create a matching dictionary that specifies any IOService of a class, or its subclasses. The class is specified by C-string name. + @param name The class name, as a const C-string. Class matching is successful on IOService's of this class or any subclass. + @result The matching dictionary created, is returned on success, or zero on failure. The dictionary is commonly passed to IOServiceGetMatchingServices or IOServiceAddNotification which will consume a reference, otherwise it should be released with CFRelease by the caller. */ + + // Serial devices are instances of class IOSerialBSDClient + classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue); + if (classesToMatch == NULL) + { + printf("IOServiceMatching returned a NULL dictionary.\n"); + } + else { +/*! + @function CFDictionarySetValue + Sets the value of the key in the dictionary. + @param theDict The dictionary to which the value is to be set. If this + parameter is not a valid mutable CFDictionary, the behavior is + undefined. If the dictionary is a fixed-capacity dictionary and + it is full before this operation, and the key does not exist in + the dictionary, the behavior is undefined. + @param key The key of the value to set into the dictionary. If a key + which matches this key is already present in the dictionary, only + the value is changed ("add if absent, replace if present"). If + no key matches the given key, the key-value pair is added to the + dictionary. If added, the key is retained by the dictionary, + using the retain callback provided + when the dictionary was created. If the key is not of the sort + expected by the key retain callback, the behavior is undefined. + @param value The value to add to or replace into the dictionary. The value + is retained by the dictionary using the retain callback provided + when the dictionary was created, and the previous value if any is + released. If the value is not of the sort expected by the + retain or release callbacks, the behavior is undefined. +*/ +// CFDictionarySetValue(classesToMatch, +// CFSTR(kIOSerialBSDTypeKey), +// CFSTR(kIOSerialBSDModemType)); + + // Each serial device object has a property with key + // kIOSerialBSDTypeKey and a value that is one of kIOSerialBSDAllTypes, + // kIOSerialBSDModemType, or kIOSerialBSDRS232Type. You can experiment with the + // matching by changing the last parameter in the above call to CFDictionarySetValue. + + // As shipped, this sample is only interested in modems, + // so add this property to the CFDictionary we're matching on. + // This will find devices that advertise themselves as modems, + // such as built-in and USB modems. However, this match won't find serial modems. + } + + /*! @function IOServiceGetMatchingServices + @abstract Look up registered IOService objects that match a matching dictionary. + @discussion This is the preferred method of finding IOService objects currently registered by IOKit. IOServiceAddNotification can also supply this information and install a notification of new IOServices. The matching information used in the matching dictionary may vary depending on the class of service being looked up. + @param masterPort The master port obtained from IOMasterPort(). + @param matching A CF dictionary containing matching information, of which one reference is consumed by this function. IOKitLib can contruct matching dictionaries for common criteria with helper functions such as IOServiceMatching, IOOpenFirmwarePathMatching. + @param existing An iterator handle is returned on success, and should be released by the caller when the iteration is finished. + @result A kern_return_t error code. */ + + kernResult = IOServiceGetMatchingServices(kIOMasterPortDefault, classesToMatch, matchingServices); + if (KERN_SUCCESS != kernResult) + { + printf("IOServiceGetMatchingServices returned %d\n", kernResult); + goto exit; + } + +exit: + return kernResult; +} + +// Given an iterator across a set of modems, return the BSD path to the first one. +// If no modems are found the path name is set to an empty string. +static kern_return_t GetModemPath(io_iterator_t serialPortIterator, char *bsdPath, CFIndex maxPathSize, QComboBox *box) +{ + io_object_t modemService; + kern_return_t kernResult = KERN_FAILURE; + Boolean modemFound = false; + + // Initialize the returned path + *bsdPath = '\0'; + + // Iterate across all modems found. In this example, we bail after finding the first modem. + + while ((modemService = IOIteratorNext(serialPortIterator)) && !modemFound) + { + CFTypeRef bsdPathAsCFString; + + // Get the callout device's path (/dev/cu.xxxxx). The callout device should almost always be + // used: the dialin device (/dev/tty.xxxxx) would be used when monitoring a serial port for + // incoming calls, e.g. a fax listener. + + bsdPathAsCFString = IORegistryEntryCreateCFProperty(modemService, + CFSTR(kIOCalloutDeviceKey), + kCFAllocatorDefault, + 0); + if (bsdPathAsCFString) + { + Boolean result; + + // Convert the path from a CFString to a C (NUL-terminated) string for use + // with the POSIX open() call. + + result = CFStringGetCString((const __CFString*) bsdPathAsCFString, + bsdPath, + maxPathSize, + kCFStringEncodingUTF8); + CFRelease(bsdPathAsCFString); + + if (result) { + box->addItem(bsdPath); + } + } + + printf("\n"); + + // Release the io_service_t now that we are done with it. + +// (void) IOObjectRelease(modemService); + } + + return kernResult; +} + +#include "mainwindow.h" +void MainWindow::osLoadDeviceNameCombos(QComboBox *box) +{ + kern_return_t kernResult; + io_iterator_t serialPortIterator; + char bsdPath[MAXPATHLEN]; + + kernResult = FindModems(&serialPortIterator); + kernResult = GetModemPath(serialPortIterator, bsdPath, sizeof(bsdPath), box); + +} diff --git a/gui/upgrade.cpp b/gui/upgrade.cpp index f2bcdaa1b..7635b6eff 100644 --- a/gui/upgrade.cpp +++ b/gui/upgrade.cpp @@ -1,5 +1,5 @@ // -*- C++ -*- -// $Id: upgrade.cpp,v 1.4 2009/08/06 01:54:06 robertl Exp $ +// $Id: upgrade.cpp,v 1.5 2009/08/06 03:19:10 robertl Exp $ /* Copyright (C) 2009 Robert Lipe, robertlipe@gpsbabel.org @@ -162,7 +162,9 @@ void UpgradeCheck::httpRequestFinished(int requestId, bool error) if(updateVersionNum > currentVersionNum && updateCandidate) { response = tr("
A new version of GPSBabel is available
" "Your version is %1
" - "The latest version is %2
") + "The latest version is %2" + "
" + "Download GPSBabel now.
") .arg(currentVersion) .arg(updateVersion); @@ -171,7 +173,11 @@ void UpgradeCheck::httpRequestFinished(int requestId, bool error) } if (response.length()) { - QMessageBox::information(0, tr("Upgrade"), response); + QMessageBox information; + information.setWindowTitle(tr("Upgrade")); + information.setTextFormat(Qt::RichText); + information.setText(response); + information.exec(); upgradeWarningTime = QDateTime(QDateTime::currentDateTime()); } -- 2.30.2